﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Ultima.Xna.Entities;

namespace Ultima.Xna.Net.Packets
{
    public class AsciiSpeechtPacket : SendPacket
    {
        public AsciiSpeechtPacket(short color, short font, string lang, string text)
            : base(0xAD, "Ascii Speech")
        {
            Stream.Write((byte)0);
            Stream.Write((short)color);
            Stream.Write((short)font);
            Stream.WriteAsciiNull(lang);
            Stream.Write((byte)0);
            Stream.Write((byte)0x10);
            Stream.Write((byte)0x02);
            Stream.WriteAsciiNull(text);
        }
    }

    public class AttackRequestPacket : SendPacket
    {
        public AttackRequestPacket(Serial serial)
            : base(0x05, "Attack Request", 5)
        {
            Stream.Write(serial);
        }
    }

    public class BuyItemsPacket : SendPacket
    {
        public BuyItemsPacket(Serial vendorSerial, Pair<int, short>[] items)
            : base(0x3B, "Buy Items")
        {
            Stream.Write(vendorSerial);
            Stream.Write((byte)0x02); // flag

            for (int i = 0; i < items.Length; i++)
            {
                Stream.Write((byte)0x1A); // layer?
                Stream.Write(items[i].ItemA);
                Stream.Write((short)items[i].ItemB);
            }
        }
    }

    public class ClientVersionPacket : SendPacket
    {
        public ClientVersionPacket(string version)
            : base(0xBD, "Client Version")
        {
            Stream.WriteAsciiNull(version);
        }
    }

    public class CreateCharacterPacket : SendPacket
    {
        public CreateCharacterPacket(string name, Sex sex, Race race,
            byte str, byte dex, byte intel, byte skill1, byte skill1Value,
            byte skill2, byte skill2Value, byte skill3, byte skill3Value, short skinColor,
            short hairStyle, short hairColor, short facialHairStyle, short facialHairColor,
            short locationIndex, short slotNumber, int clientIp, short shirtColor, short pantsColor)
            : base(0x00, "Create Character", 104)
        {
            str = (byte)MathHelper.Clamp(str, 10, 45);
            dex = (byte)MathHelper.Clamp(dex, 10, 45);
            intel = (byte)MathHelper.Clamp(intel, 10, 45);

            if (str + dex + intel > 65)
                throw new Exception("Unable to create character with a combined stat total greater than 65");

            Stream.Write(0xedededed);
            Stream.Write(0xffffffff);
            Stream.Write((byte)0);
            Stream.WriteAsciiFixed(name, 30);
            Stream.WriteAsciiFixed("", 30);
            Stream.Write((byte)((int)sex + (int)race));
            Stream.Write((byte)str);
            Stream.Write((byte)dex);
            Stream.Write((byte)intel);

            Stream.Write((byte)skill1);
            Stream.Write((byte)skill1Value);
            Stream.Write((byte)skill2);
            Stream.Write((byte)skill2Value);
            Stream.Write((byte)skill3);
            Stream.Write((byte)skill3Value);

            Stream.Write((short)skinColor);
            Stream.Write((short)hairStyle);
            Stream.Write((short)hairColor);
            Stream.Write((short)facialHairStyle);
            Stream.Write((short)facialHairColor);
            Stream.Write((short)locationIndex);
            Stream.Write((short)slotNumber);
            Stream.Write((short)0);

            Stream.Write(clientIp);
            Stream.Write((short)shirtColor);
            Stream.Write((short)pantsColor);
        }
    }

    public class DeleteCharacterPacket : SendPacket
    {
        public DeleteCharacterPacket(string password, int characterIndex, int clientIp)
            : base(0x83, "Delete Character", 39)
        {
            Stream.WriteAsciiFixed(password, 30);
            Stream.Write(characterIndex);
            Stream.Write(clientIp);
        }
    }

    public class DisconnectNotificationPacket : SendPacket
    {
        public DisconnectNotificationPacket(Serial followed, Serial follower)
            : base(0x15, "Disconnect Notification", 9)
        {
            Stream.Write(followed);
            Stream.Write(follower);
        }
    }

    public class DoubleClickPacket : SendPacket
    {
        public DoubleClickPacket(Serial serial)
            : base(0x06, "Double Click", 5)
        {
            Stream.Write(serial);
        }
    }

    public class DropItemPacket : SendPacket
    {
        public DropItemPacket(Serial serial, short x, short y, byte z, byte gridIndex, Serial containerSerial)
            : base(0x08, "Drop Item", 14)
        {
            Stream.Write(serial);
            Stream.Write((short)x);
            Stream.Write((short)y);
            Stream.Write((byte)z);
            Stream.Write((byte)gridIndex);
            Stream.Write(containerSerial);
        }
    }

    public class DropToLayerPacket : SendPacket
    {
        public DropToLayerPacket(Serial itemSerial, byte layer, Serial playerSerial)
            : base(0x13, "Drop To Layer", 10)
        {
            Stream.Write(itemSerial);
            Stream.Write((byte)layer);
            Stream.Write(playerSerial);
        }
    }

    public class GameLoginPacket : SendPacket
    {
        public GameLoginPacket(int authId, string username, string password)
            : base(0x91, "Game Server Login", 0x41)
        {
            Stream.Write(authId);
            Stream.WriteAsciiFixed(username, 30);
            Stream.WriteAsciiFixed(password, 30);
        }

    }

    public class GetPlayerStatusPacket : SendPacket
    {
        public GetPlayerStatusPacket(byte type, Serial serial)
            : base(0x34, "Get Player Status", 10)
        {
            Stream.Write((int)0);//Unknown
            Stream.Write((byte)type);
            Stream.Write(serial);
        }
    }

    public class GumpMenuSelectPacket : SendPacket
    {
        public GumpMenuSelectPacket(int id, int gumpId, int buttonId, int[] switchIds, Pair<short, string>[] textEntries)
            : base(0xB1, "Gump Menu Select")
        {
            Stream.Write(id);
            Stream.Write(gumpId);
            Stream.Write(buttonId);
            Stream.Write(switchIds.Length);

            for (int i = 0; i < switchIds.Length; i++)
                Stream.Write(switchIds[i]);

            Stream.Write(textEntries.Length);

            for (int i = 0; i < textEntries.Length; i++)
            {
                int length = textEntries[i].ItemB.Length * 2;

                Stream.Write(textEntries[i].ItemA);
                Stream.Write(length);
                Stream.WriteBigUniFixed(textEntries[i].ItemB, length);
            }
        }
    }

    public class GumpTextEntryDialogReplyPacket : SendPacket
    {
        public GumpTextEntryDialogReplyPacket(int id, byte type, byte index, string reply)
            : base(0xAC, "Gump TextEntry Dialog Reply")
        {
            Stream.Write(id);
            Stream.Write((byte)type);
            Stream.Write((byte)index);
            Stream.Write((short)0x0000);
            Stream.Write((byte)0x00);
            Stream.WriteAsciiNull(reply);
        }
    }

    public class LoginCharacterPacket : SendPacket
    {
        public LoginCharacterPacket(string name, int index, int ipAddress)
            : base(0x5d, "Character Select", 0x49)
        {
            Stream.Write((uint)0xedededed);
            Stream.WriteAsciiFixed(name, 30);
            Stream.Write((short)0);
            Stream.Write((int)0x1f);
            Stream.Write((int)1);
            Stream.Write((int)0x18);
            Stream.WriteAsciiFixed("", 0x10);
            Stream.Write(index);
            Stream.Write(ipAddress);
        }
    }

    public class LoginPacket : SendPacket
    {
        public LoginPacket(string username, string password)
            : base(0x80, "Account Login", 0x3E)
        {
            this.Stream.WriteAsciiFixed(username, 30);
            this.Stream.WriteAsciiFixed(password, 30);
            this.Stream.Write((byte)0x5D);
        }
    }

    public class MoveRequestPacket : SendPacket
    {
        public MoveRequestPacket(byte direction, byte sequence, int fastWalkPreventionKey)
            : base(0x02, "Move Request", 7)
        {
            Stream.Write((byte)direction);
            Stream.Write((byte)sequence);
            Stream.Write(fastWalkPreventionKey);
        }
    }

    public class PathfindingPacket : SendPacket
    {
        public PathfindingPacket(short x, short y, short z)
            : base(0x38, "Pathfinding", 7)
        {
            Stream.Write((short)x);
            Stream.Write((short)y);
            Stream.Write((short)z);
        }
    }

    public class PickupItemPacket : SendPacket
    {
        public PickupItemPacket(Serial serial, short count)
            : base(0x07, "Pickup Item", 7)
        {
            Stream.Write(serial);
            Stream.Write((short)count);
        }
    }

    public class QueryPropertiesPacket : SendPacket
    {
        public QueryPropertiesPacket(Serial serial)
            : base(0xD6, "Query Properties", 7)
        {
            Stream.Write((short)7);
            Stream.Write((int)serial);
        }
    }

    public class RenameCharacterPacket : SendPacket
    {
        public RenameCharacterPacket(Serial serial, string name)
            : base(0x75, "Rename Request", 35)
        {
            Stream.Write(serial);
            Stream.WriteAsciiFixed(name, 30);
        }
    }
    
    public class RequestContextMenuPacket : SendPacket
    {
        public RequestContextMenuPacket(Serial serial)
            : base(0xBF, "Context Menu Request")
        {
            Stream.Write((short)0x13);
            Stream.Write(serial);
        }
    }

    public class RequestHelpPacket : SendPacket
    {
        public RequestHelpPacket()
            : base(0x9B, "Request Help", 258)
        {
            byte[] empty = new byte[257];
            Stream.Write(empty, 0, 257);
        }
    }

    public class RequestNamePacket : SendPacket
    {
        public RequestNamePacket(Serial serial)
            : base(0x98, "Request Name", 7)
        {
            Stream.Write((short)7);
            Stream.Write((int)serial);
        }
    }

    public class RequestNoticePacket : SendPacket
    {
        public RequestNoticePacket(short lastNoticeNumber)
            : base(0xA7, "Request Notice", 4)
        {
            Stream.Write((short)lastNoticeNumber);
            Stream.Write((byte)0x01);
        }
    }

    public class RequestSkillUsePacket : SendPacket
    {
        public RequestSkillUsePacket(int id)
            : base(0x12, "Request Skill Use")
        {
            Stream.Write((byte)0x24);
            Stream.WriteAsciiNull(String.Format("{0} 0", id));
        }
    }

    public class RequestTipPacket : SendPacket
    {
        public RequestTipPacket(short lastTipNumber)
            : base(0xA7, "Request Tip", 4)
        {
            Stream.Write((short)lastTipNumber);
            Stream.Write((byte)0x00);
        }
    }

    public class RequestWarModePacket : SendPacket
    {
        public RequestWarModePacket(bool warMode)
            : base(0x72, "Request War Mode", 5)
        {
            Stream.Write(warMode);
            Stream.Write((byte)0x00);
            Stream.Write((byte)0x32);
            Stream.Write((byte)0x00);
        }
    }
    
    public class ResponseToDialogBoxPacket : SendPacket
    {
        public ResponseToDialogBoxPacket(int dialogId, short menuId, short index, short modelNum, short color)
            : base(0x7D, "Response To Dialog Box", 13)
        {
            Stream.Write(dialogId);
            Stream.Write((short)menuId);
            Stream.Write((short)index);
            Stream.Write((short)modelNum);
            Stream.Write((short)color);
        }
    }
    
    public class SeedPacket : SendPacket
    {
        public SeedPacket(int seed, int major, int minor, int revision, int prototype)
            : base(0xEF, "Seed", 21)
        {
            Stream.Write(seed);
            Stream.Write(major);
            Stream.Write(minor);
            Stream.Write(revision);
            Stream.Write(prototype);
        }
    }

    public class SelectServerPacket : SendPacket
    {
        public SelectServerPacket(int id) :
            base(0xA0, "Select Server", 3)
        {
            Stream.Write((short)id);
        }
    }

    public class SellListReplyPacket : SendPacket
    {
        public SellListReplyPacket(Serial vendorSerial, Pair<int, short>[] items)
            : base(0x9F, "Sell List Reply")
        {
            Stream.Write(vendorSerial);
            Stream.Write((short)items.Length);

            for (int i = 0; i < items.Length; i++)
            {
                Stream.Write(items[i].ItemA);
                Stream.Write((short)items[i].ItemB);
            }
        }
    }

    public class SingleClickPacket : SendPacket
    {
        public SingleClickPacket(Serial serial)
            : base(0x09, "Single Click", 5)
        {
            Stream.Write(serial);
        }
    }
    
    public class TalkRequestPacket : SendPacket
    {
        public TalkRequestPacket(byte speechType, short color, short font, string message)
            : base(0x03, "Talk Request")
        {
            Stream.Write((byte)speechType);
            Stream.Write((short)color);
            Stream.Write((short)font);
            Stream.WriteAsciiNull(message);
        }
    }

    public class UnicodeSpeechPacket : SendPacket
    {
        public UnicodeSpeechPacket(short color, short font, string lang, string text)
            : base(0xAD, "Unicode Speech")
        {
            Stream.Write((byte)0);
            Stream.Write((short)color);
            Stream.Write((short)font);
            Stream.WriteAsciiNull(lang);
            // Stream.Write((byte)0);
            // Stream.Write((byte)0x10);
            // Stream.Write((byte)0x02);
            Stream.WriteBigUniNull(text);
        }
    }

    public class UOGInfoRequestPacket : SendPacket
    {
        public UOGInfoRequestPacket()
            : base(0xF1, "UOG Information Request", 4)
        {
            Stream.Write((short)4);
            Stream.Write((byte)0xFF);
        }
    }
}
